package com.mybatis.boost.mapper.provider;

import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.ibatis.mapping.MappedStatement;
import tk.mybatis.mapper.mapperhelper.MapperHelper;

import static com.mybatis.boost.mapper.SingleBaseMapper.PARAM_CONDITION;
import static com.mybatis.boost.mapper.SingleBaseMapper.PARAM_RECORD;
import static com.mybatis.boost.mapper.util.sql.DeleteSqlUtil.deleteFromTable;
import static com.mybatis.boost.mapper.util.sql.InsertSqlUtil.*;
import static com.mybatis.boost.mapper.util.sql.SelectSqlUtil.*;
import static com.mybatis.boost.mapper.util.sql.SqlUtil.checkDefaultParamValue;
import static com.mybatis.boost.mapper.util.sql.SqlUtil.checkParamValue;
import static com.mybatis.boost.mapper.util.sql.UpdateSqlUtil.*;

/**
 * 单表通用CRUD的SQL生成（加载期生成，无时间性能损耗）
 *
 * @author jearton
 * @since 2016/11/21
 */
@Slf4j
@SuppressWarnings("unused")
public class SingleSqlProvider extends SingleMapperTemplate {

    public SingleSqlProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
        super(mapperClass, mapperHelper);
    }

    public String selectOptionalById(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        setResultType(ms, entityClass);
        return checkDefaultParamValue() +
                selectAllColumns(entityClass) +
                fromTable(entityClass, tableName(entityClass)) +
                wherePKColumns(entityClass);
    }

    public String selectOptional(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        setResultType(ms, entityClass);
        return checkDefaultParamValue() +
                selectColumnsByCondition(entityClass) +
                fromTable(entityClass, tableName(entityClass)) +
                conditionWhereClause();
    }

    public String selectOptionalByField(MappedStatement ms) {
        return selectByField(ms);
    }

    public String selectByField(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        setResultType(ms, entityClass);
        return checkField() +
                selectAllColumns(entityClass) +
                fromTable(entityClass, tableName(entityClass)) +
                whereSingleColumn(entityClass);
    }

    public String select(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        setResultType(ms, entityClass);
        return checkDefaultParamValue() +
                selectColumnsByCondition(entityClass) +
                fromTable(entityClass, tableName(entityClass)) +
                conditionWhereClause() +
                conditionOrderByClause() +
                conditionLimitByClause();
    }

    public String count(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkDefaultParamValue() +
                selectCount(entityClass) +
                fromTable(entityClass, tableName(entityClass)) +
                conditionWhereClause();
    }

    public String countByField(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkField() +
                selectCount(entityClass) +
                fromTable(entityClass, tableName(entityClass)) +
                whereSingleColumn(entityClass);
    }

    /**
     * 单条insert原则：
     * 1. 如果数据库列定义为NOT NULL，则加if not null判断；反之，不加判断；
     * 2. 对于current_timestamp的列无需出现在insert语句中。
     * <p>
     * 动态insert语句如下：
     * <pre>
     *      <!--主键回写(如果有指定的话)-->
     *      <selectKey keyProperty="id" order="AFTER" resultType="long">
     *          <choose>
     *              <when test="id == null">
     *                  SELECT LAST_INSERT_ID()
     *              </when>
     *              <otherwise>
     *                  SELECT #{id,javaType=java.lang.Long}
     *              </otherwise>
     *          </choose>
     *      </selectKey>
     *
     *      <!--uuid回写（如果有且没有手动设置过的话）-->
     *      <if test='tradeNo == null and _parameter.tradeNo = @java.util.UUID@randomUUID().toString().replace("-", "")'/>
     *
     *      <!--insert语句-->
     *      INSERT INTO
     *      <if test="@tk.mybatis.mapper.util.OGNL@isDynamicParameter(_parameter) and @jodd.util.StringUtil@isNotBlank(dynamicTableName)">
     *          ${dynamicTableName}
     *      </if>
     *      <if test="@tk.mybatis.mapper.util.OGNL@isNotDynamicParameter(_parameter) or @jodd.util.StringUtil@isBlank(dynamicTableName)">
     *          `fc_pay_audit`
     *      </if>
     *
     *      <trim prefix="(" suffix=")" suffixOverrides=",">
     *          <if test="id != null">`id`,</if>
     *          <if test="status != null">`status`,</if>
     *          <if test="tradeNo != null">`trade_no`,</if>
     *          `trade_time`,
     *          <if test="remark != null">`remark`,</if>
     *      </trim>
     *
     *      <trim prefix="VALUES(" suffix=")" suffixOverrides=",">
     *          <if test="id != null">#{id,javaType=java.lang.Long},</if>
     *          <if test="status != null">#{status,javaType=com.xxx.model.enums.AuditStatus},</if>
     *          <if test="tradeNo != null">#{tradeNo,javaType=java.lang.String},</if>
     *          #{tradeTime,javaType=java.time.ZonedDateTime},
     *          <if test="remark != null">#{remark,javaType=java.lang.String},</if>
     *      </trim>
     * </pre>
     */
    public String insert(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        //主键回写
        getIdentityColumn(entityClass).ifPresent(column -> newSelectKeyMappedStatement(ms, column));
        //拼动态SQL
        return checkDefaultParamValue() +
                insertUniqueId(entityClass, getUUID()) +
                insertIntoTable(entityClass, tableName(entityClass)) +
                insertColumns(entityClass) +
                insertValues(entityClass);
    }

    public String insertIgnore(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        //主键回写
        getIdentityColumn(entityClass).ifPresent(column -> newSelectKeyMappedStatement(ms, column));
        //拼动态SQL
        return checkDefaultParamValue() +
                insertUniqueId(entityClass, getUUID()) +
                insertIgnoreIntoTable(entityClass, tableName(entityClass)) +
                insertColumns(entityClass) +
                insertValues(entityClass);
    }

    public String insertOnDuplicateKeyUpdate(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        //主键回写
        getIdentityColumn(entityClass).ifPresent(column -> newSelectKeyMappedStatement(ms, column));
        //拼动态SQL
        return checkDefaultParamValue() +
                insertUniqueId(entityClass, getUUID()) +
                insertIntoTable(entityClass, tableName(entityClass)) +
                insertColumns(entityClass) +
                insertValues(entityClass) +
                onDuplicateKeyUpdate(entityClass);
    }

    public String insertBatch(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        //主键回写
        getIdentityColumn(entityClass).ifPresent(column -> useGeneratedKeys(ms, column));
        //拼动态SQL
        return insertBatchIntoTable(tableName(entityClass)) +
                insertBatchColumns(entityClass) +
                insertBatchValues(entityClass, getUUID());
    }

    public String insertBatchIgnore(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        //主键回写
        getIdentityColumn(entityClass).ifPresent(column -> useGeneratedKeys(ms, column));
        //拼动态SQL
        return insertBatchIgnoreIntoTable(tableName(entityClass)) +
                insertBatchColumns(entityClass) +
                insertBatchValues(entityClass, getUUID());
    }

    /**
     * update条件：主键=xxx[，动态表名=xxx]
     * 单条update set原则：
     * 1. 如果数据库列定义为NOT NULL 或 明确指定不允许更新为null，则加if not null判断；反之，不加判断；
     * 2. 对于current_timestamp的列无需出现在update语句中。
     */
    public String updateById(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkParamValue(PARAM_RECORD) +
                checkPKValue(entityClass, PARAM_RECORD) +
                updateTable(entityClass, tableName(entityClass), PARAM_RECORD) +
                updateSetColumns(entityClass) +
                wherePKColumns(entityClass, PARAM_RECORD);
    }

    /**
     * update条件：任意指定条件
     * 单条update set原则：
     * 1. 如果数据库列定义为NOT NULL 或 明确指定不允许更新为null，则加if not null判断；反之，不加判断；
     * 2. 对于current_timestamp的列无需出现在update语句中。
     */
    public String update(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkParamValue(PARAM_RECORD, PARAM_CONDITION) +
                updateTable(entityClass, tableName(entityClass), PARAM_CONDITION) +
                updateSetColumns(entityClass) +
                conditionWhereClause(PARAM_CONDITION);
    }

    /**
     * 更新单个字段
     */
    public String updateField(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkField() +
                checkParamValue(PARAM_CONDITION) +
                updateTable(entityClass, tableName(entityClass), PARAM_CONDITION) +
                updateSetSingleColumn(entityClass) +
                conditionWhereClause(PARAM_CONDITION);
    }

    public String deleteById(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkDefaultParamValue() + deleteFromTable(entityClass, tableName(entityClass)) + wherePKColumns(entityClass);
    }

    public String delete(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkDefaultParamValue() + deleteFromTable(entityClass, tableName(entityClass)) + conditionWhereClause();
    }

    public String deleteByField(MappedStatement ms) {
        val entityClass = getEntityClass(ms);
        return checkField() + deleteFromTable(entityClass, tableName(entityClass)) + whereSingleColumn(entityClass);
    }
}
